﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttlang.type;
using ttlang.type.code;

namespace ttlang.compiler
{
    //此处处理函数内部代码的编译
    internal static class compiler_codeblock
    {
        public static void ParseNode_CodeBlock(CompilerContext context, src.AstNode innode, out type.code.TTCode outblock)
        {
            //每入一層括號，加個棧
            context.PushBlock();

            outblock = new type.code.TTCode();
            outblock.type = CodeType.CODEBLOCK;
           
            //该脱的早都脱过了
            ////兼容脱括号
            //if (innode.type == "code_block")
            //{
            //    innode = innode.children[1];
            //}

            //if (innode.type != "codedoc")
            //{
            //    throw new Exception("must be codedoc");
            //}
            List<TTCode> codes = new List<TTCode>();
            if (innode.children != null)
            {
                for (var i = 1; i < innode.children.Count - 1; i++)
                {
                    var item = innode.children[i];
                    if (item.type == "codedoc")
                    {
                        foreach (var subitem in item.children)
                        {
                            ParseNode_CodeAtom(context, subitem, out type.code.TTCode atom);
                            if (atom == null)
                                continue;//空行直接走，不要了
                            codes.Add(atom);
                        }
                    }
                    else
                    {//有时候会有把codedoc直接展开的情况
                        ParseNode_CodeAtom(context, item, out type.code.TTCode atom);
                        if (atom == null)
                            continue;//空行直接走，不要了
                        codes.Add(atom);
                    }

                }
            }
            outblock.elements =new CodeElement[]{ CodeElement.TTCodeArray(codes)};

            context.PopBlock();
        }
        public static void ParseNode_CodeAtom(CompilerContext context, src.AstNode innode, out type.code.TTCode outnode)
        {
            outnode = null;
            //脱層
            if (innode.text == ";")
                return;
            if (innode.type == "code_atom")
            {
                innode = innode.children[0];
            }
            switch (innode.type)
            {
                case "define":
                    throw new NotImplementedException();
                case "COMMENTSINGLE":
                    {
                        var comment = new type.code.TTCode();
                        comment.type = CodeType.CODE_COMMENT;
                        comment.elements = new type.code.CodeElement[1];
                        comment.elements[0] = CodeElement.Other_String(innode.text);
                        comment.elements[0].helpinfo = "SINGLELINE";
                        outnode = comment;
                    }
                    break;
                case "COMMENTMUL":
                    {
                        var comment = new type.code.TTCode();
                        comment.type = CodeType.CODE_COMMENT;
                        comment.elements = new type.code.CodeElement[1];
                        comment.elements[0] = CodeElement.Other_String(innode.text);
                        comment.elements[0].helpinfo = "RANGE";
                        outnode = comment;
                    }

                    break;
                case "empty_line":
                    outnode = null;
                    break;
                case "codeelem"://直接脫

                    if (innode.children == null || innode.children.Count == 0)
                        break;
                    ParseNode_CodeElem(context, innode.children[0], out outnode);
                    break;
                case "code_vaild":
                    ParseNode_CodeAtom(context, innode.children[0], out outnode);
                    break;
                case "codeelem_value_def":
                    {
                        ParseNode_CodeElem_ValueDef(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_set":
                    {
                        ParseNode_CodeElem_Set(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_selfop":
                    {
                        ParseNode_CodeElem_SelfOp(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_expr":
                    {
                        ParseNode_CodeElem_Expr(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;

                case "code_return":
                    {
                        ParseNode_CodeElem_Return(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_if":
                    {
                        ParseNode_CodeIf(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_for":
                    {
                        ParseNode_CodeFor(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "code_block_while":
                    throw new NotImplementedException();
                case "code_block_dowhile":
                    throw new NotImplementedException();
                case "code_block_switch":
                    throw new NotImplementedException();
                case "code_block":
                    {
                        ParseNode_CodeBlock(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                default:
                    throw new NotImplementedException();

            }
        }
        //普通的代碼行
        public static void ParseNode_CodeElem(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            switch (innode.type)
            {
                case "codeelem_value_def":
                    {
                        ParseNode_CodeElem_ValueDef(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_set":
                    {
                        ParseNode_CodeElem_Set(context, innode, out TTCode _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_selfop":
                    {
                        ParseNode_CodeElem_SelfOp(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;
                case "codeelem_expr":
                    {
                        ParseNode_CodeElem_Expr(context, innode, out var _node);
                        outnode = _node;
                    }
                    break;

                default:
                    throw new NotImplementedException();

            }
        }
        public static void ParseNode_CodeElem_Return(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            TTCode _return = new TTCode();
            _return.type = CodeType.CODE_RETURN;
            if (innode.children.Count > 1)
            {//with value
                compiler_expr.ParseNode_Expr(context, innode.children[1], out TTCode _node);
                _return.elements = new CodeElement[1];
                _return.elements[0] = CodeElement.TTCode(_node);
            }
            outnode = _return;
        }
        public static void ParseNode_CodeElem_Set(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            TTCode def = new TTCode();
            def.type = CodeType.CODE_SET;
            def.elements = new CodeElement[2];
            def.elements[0] = CodeElement.Other_String(innode.children[0].text);


            compiler_expr.ParseNode_Expr(context, innode.children[2], out TTCode _node);
            def.elements[0] = CodeElement.TTCode(_node);

            outnode = def;
        }
        public static void ParseNode_CodeElem_ValueDef(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            TTCode def = new TTCode();
            def.type = CodeType.CODE_VALUEDEF;
          
            //innode.children[0].type == "var";//should be var
            var def_id = innode.children[1].text;//shoulde be id;
 
            TTType deftype = null;
            TTCode initvlaue=null;
            int checkeqpos = 2;
            if (innode.children.Count < 4)
                throw new Exception("CodeElem 不能没有类型或初值" + " in:" + innode.text);
            if (innode.children[2].text == ":")//withtype
            {
                var deftype_name = innode.children[3].text;
                checkeqpos = 4;
                if (context.FindType(deftype_name, out var _type) == false || _type == null)
                {
                    throw new Exception("类型未知:" + _type + " in:" + innode.text);
                }

                deftype = _type;
            }

            if (innode.children.Count > checkeqpos + 1)
            {//with value
                compiler_expr.ParseNode_Expr(context, innode.children[checkeqpos + 1], out TTCode _node);
                initvlaue = _node;
            }

            //强类型语言，expr必须有明确类型，所以直接要就完了
            if (deftype == null)
            {
                deftype = initvlaue.GetExprType();
            }
            if (deftype == null)
            {
                throw new Exception("未能通過右值推斷類型");
            }

            def.elements = new CodeElement[initvlaue!=null?3:2];
            def.elements[0] = CodeElement.Other_String(def_id);
            def.elements[1] = CodeElement.TTType(deftype);
            if(initvlaue!=null)
                 def.elements[2] = CodeElement.TTCode(initvlaue);

            context.RegTempVar(def_id,deftype);
            outnode = def;
        }
        public static void ParseNode_CodeElem_SelfOp(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            TTCode self = new TTCode();
            self.type = CodeType.CODE_SELFOP;
            compiler_expr.ParseNode_Expr(context, innode.children[0], out var self_id_expr);
            var self_token = innode.children[1].text;
            TTCode self_rightvalue = null;
            if (innode.children.Count > 2)
                compiler_expr.ParseNode_Expr(context, innode.children[2], out self_rightvalue);

            self.elements = new CodeElement[self_rightvalue != null ? 3 : 2];
            self.elements[0] = CodeElement.TTCode(self_id_expr);
            self.elements[1] = CodeElement.Other_String(self_token);
            if (self_rightvalue != null)
                self.elements[2] = CodeElement.TTCode(self_rightvalue);

            outnode = self;

        }
        public static void ParseNode_CodeIf(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            TTCode fif = new TTCode();
            fif.type = CodeType.CODE_IF;

            compiler_expr.ParseNode_Expr(context, innode.children[2], out var fif_elem_cond);
            ParseNode_CodeAtom(context, innode.children[4], out var fif_body);

            TTCode fif_elsepart=null;
            if (innode.children.Count > 5 && innode.children[5].text == "else")
            {
                ParseNode_CodeAtom(context, innode.children[6], out  fif_elsepart);
            }
            fif.elements = new CodeElement[fif_elsepart != null ? 3 : 2];
            fif.elements[0] = CodeElement.TTCode(fif_elem_cond);
            fif.elements[1] = CodeElement.TTCode(fif_body);
            if (fif_elsepart != null)
                fif.elements[2] = CodeElement.TTCode(fif_elsepart);

            outnode = fif;
        }
        public static void ParseNode_CodeFor(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            context.PushBlock();

            TTCode ffor = new TTCode();
            ffor.type = CodeType.CODE_FOR;
            //0 for
            //1 (
            src.AstNode initelem;
            src.AstNode condelem;
            src.AstNode opelem;
            src.AstNode body;
            int condseek = 0;
            int opseek = 0;
            int bodyseek = 0;
            initelem = innode.children[2];
            if (initelem.text == ";")
            {
                initelem = null;
                condseek = 3;
            }
            else
            {
                condseek = 4;
            }
            condelem = innode.children[condseek];
            if (condelem.text == ";")
            {
                condelem = null;
                opseek = condseek + 1;
            }
            else
            {
                opseek = condseek + 2;
            }
            opelem = innode.children[opseek];
            if (opelem.text == ")")
            {
                opelem = null;
                bodyseek = opseek + 1;
            }
            else
            {
                bodyseek = opseek + 2;
            }
            body = innode.children[bodyseek];
            if (body.text == ";")
            {
                body = null;
            }
            TTCode ffor_elem_init = null;
            TTCode ffor_elem_cond = null;
            TTCode ffor_elem_step = null;
            TTCode _body = null;
            if (initelem != null)
                ParseNode_CodeAtom(context, initelem, out  ffor_elem_init);

            if (condelem != null)
                compiler_expr.ParseNode_Expr(context, condelem, out  ffor_elem_cond);

            if (opelem != null)
                ParseNode_CodeAtom(context, opelem, out  ffor_elem_step);

            if (body != null)
            {
                ParseNode_CodeAtom(context, body, out  _body);
                if (_body == null)
                    throw new Exception("should not null.");
                
            }
            ffor.elements = new CodeElement[4];
            ffor.elements[0] = CodeElement.TTCode(ffor_elem_init);
            ffor.elements[1] = CodeElement.TTCode(ffor_elem_cond);
            ffor.elements[2] = CodeElement.TTCode(ffor_elem_step);
            ffor.elements[3] = CodeElement.TTCode(_body);
            outnode = ffor;

            context.PopBlock();
        }
        public static void ParseNode_CodeElem_Expr(CompilerContext context, src.AstNode innode, out TTCode outnode)
        {
            var expr = innode.children[0];//
            //expr 的可能性很多
            compiler_expr.ParseNode_Expr(context, expr, out TTCode _outnode);

            if (_outnode.type == CodeType.EXPR_CALL)
            {
                var outnode_call = new TTCode();
                outnode_call.type = CodeType.CODE_CALL;
                outnode_call.elements = new CodeElement[1];
                outnode_call.elements[0] = CodeElement.TTCode(_outnode);
                outnode = outnode_call;
                return;
            }
            else
            {
                //var outnode_call = new node.nod_code();
                //compiler_expr.ParseNode_Expr_Member(context, innode, out outnode);//函数用0表达式解析
                //return;
            }

            //Code_call节点就只是简单的包裹了一个Expr_call
            //因为 code 和 expr 是两条继承链，为了从逻辑上解释清楚，做了一层包裹


            throw new Exception("not support expr type." + innode.text);

        }

    }
}
